﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Reflexive.IO
{
    public class RetryableTask<T>
    {
        T _result;
        Timer _timer;
        Func<T> _func;
        bool _success = false;
        CancellationToken _token;
        System.Threading.ManualResetEvent _event;

        public RetryableTask(Func<T> func, CancellationToken token, TimeSpan interval, int? count)
        {
            _func = func;
            _token = token;
            _timer = new Timer((state) =>
            {
                try
                {
                    _result = func();
                    _success = true;
                    _event.Set();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.TraceError(ex.ToString());
                    if (count.HasValue)
                    {
                        if (count > 0)
                        {
                            count--;
                        }
                        else
                        {
                            _event.Set();
                            return;
                        }
                    }
                    _timer.Change((TimeSpan)state, Timeout.InfiniteTimeSpan);
                }

            }, interval, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);

            _event = new System.Threading.ManualResetEvent(false);
            _token.Register(() => {
                _event.Set();
            }, true);
            
        }


        public T Result
        {
            get
            {
                _timer.Change(new TimeSpan(0), Timeout.InfiniteTimeSpan);
                _event.WaitOne();
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
                _timer.Dispose();
                if (_success)
                    return _result;
                else
                    throw new OperationCanceledException();
            }

        }


    }
}
